package org.soc.timesheet.custom.interceptor;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.reflect.MethodSignature;
import org.soc.timesheet.custom.filter.TimesheetRespHolder;
import org.soc.timesheet.domain.TokenType;
import org.soc.timesheet.domain.UsersLogged;
import org.soc.timesheet.server.domain.service.UsersLoggedService;
import org.soc.timesheet.util.ControllerAttributesContainer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * Gestisce l'aspetto che riguarda il controllo delle sessioni aperte e della presenza degli header che determinano l'autenticazione avvenuta da parte dell'utente.<br/>
 * Oltre a questo verifica che il token passato in input dal client appartenga ad una sessione ancora esistente. <br/>
 * Se questi controlli non vanno a buon fine restituisce al client un errore con relativo messaggio. <br/>
 * 
 * Inoltre si occupa anche di cancellare dalla tabella degli utenti autenticati, le sessioni scadute.
 * 
 * 
 * @author edoardo
 *
 */
public aspect ControllerJsonInterceptor {
	
	private static long TIME_SESSIONS=60L*1000L*30L;

	pointcut controller() : within(org.soc.timesheet.controller.*) && execution(ResponseEntity<String> *Json(..))  ;

	

	ResponseEntity<String> around()  : controller() {
		MethodSignature ms = (MethodSignature) thisJoinPoint.getSignature();
		try {
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
					.currentRequestAttributes()).getRequest();
			HttpServletResponse response = TimesheetRespHolder.CURRENT_RESPONSE
					.get();
			String uri = request.getRequestURI();
			String referer = request.getHeader("Referer");
			if(referer==null || referer.trim().equals("")){
				referer="http://"+request.getLocalAddr()+":"+request.getLocalPort()+"/"+request.getServletContext().getContextPath()+"/";
			}
			ControllerAttributesContainer.setReferer(referer);
			if (uri.indexOf("login") == -1) {
				String header = request.getHeader("token");
				HttpHeaders httpHeaders = new HttpHeaders();
				httpHeaders.add("Content-Type", "application/json");
				if (header == null) {
					return new ResponseEntity<String>(
							"{\"error\" : \"Authentication Required\"}",
							httpHeaders,
							HttpStatus.NETWORK_AUTHENTICATION_REQUIRED);
					// response.getOutputStream().write("{\"error\":\"Token mancante\"}".getBytes());
					// return new ResponseEntity<T>(body, headers,
					// statusCode);
				}
				UsersLogged usersLogged = UsersLogged.findUsersLoggedsByTokenEquals(header).getSingleResult();
				if(usersLogged==null){
					//se viene passato un token inesistente (che potrebbe essere non valido oppure taroccato)
					return new ResponseEntity<String>(
							"{\"error\" : \"Input Token does not exist!\"}",
							httpHeaders,
							HttpStatus.NETWORK_AUTHENTICATION_REQUIRED);
				}
				Calendar lastAccess = usersLogged.getLastAccess();
				Calendar now = GregorianCalendar.getInstance();
				TokenType tokenType = TokenType.findTokenType(usersLogged.getIdTokenType());
				long time = now.getTimeInMillis()-lastAccess.getTimeInMillis();
				
				//TODO mettere il valore della durata della sessione in qualche file di configurazione o passato come parametro dal client
				if((time>(TIME_SESSIONS) && !tokenType.getTypeDesc().equalsIgnoreCase("recover"))  ){
					//la sessione è scaduta e viene restituito un messaggio in cui si richiede il login
					return new ResponseEntity<String>(
							"{\"error\" : \"Session expired\"}",
							httpHeaders,
							HttpStatus.NETWORK_AUTHENTICATION_REQUIRED);
				}
				
				usersLogged.setLastAccess(now);
				usersLogged.setLastRequest(uri);
				//elimino tutte le sessioni utente scadute
				UsersLoggedService service = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext()).getBean(UsersLoggedService.class);
				List<UsersLogged> users = service.findAllUsersLoggeds();
				for (Iterator iterator = users.iterator(); iterator.hasNext();) {
					UsersLogged u = (UsersLogged) iterator.next();
					time = now.getTimeInMillis() - u.getLastAccess().getTimeInMillis();
					if(time>TIME_SESSIONS){
						service.deleteUsersLogged(u);
					}
				}
				return proceed();
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return null;
	}

}
